/********************************************************************************
 * @file    main.c
 * @author  jianqiang.xue
 * @Version V2.0.0
 * @Date    2022-10-22
 * @brief   上位机：<HEAD 2byte 0xA55A><FUNC 1byte> <LEN 1byte> <DATA nbyte> <SUM 1byte>
 * <FUNC>:
 *  [01h], 文件信息，包含文件大小(4byte)、文件校验和(2byte)、当前时间戳后5位(2byte)。时间戳用于双方协商一个秘钥值。
 *  [02h], 文件数据(加密后的)
 *  [03h], 校验文件，计算back区域文件是否与文件信息一致，若一致则搬运到APP区域，并跳转至APP。
 *  [E0h], 烧录口开关。如果后面<DATA>为 特定值，则打开烧录口，否则锁定烧录，防止程序为盗窃。
 *  [FFh], 如果后面<DATA>为 9581h，则程序自毁。
 *
 *  下位机返回值：<STATE 1byte> <LEN 1byte> <DATA nbyte> 0D 0A
 * 常规     成功：00 00 00 0D 0A
 * 常规     失败：01 00 00 0D 0A
 * 文件信息 成功: 00 03 <rxbuff_byte> <g_uid_l> <g_uid_h> 0D 0A
 * 文件数据 成功: 00 01 <DATA0--PACK_ID> 0D 0A
 * 其他错误码请查阅：errorno.h
 ********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "RTE_Components.h"
#include CMSIS_device_header

#include "bsp_flash.h"
#include "bsp_gpio.h"
#include "bsp_system_clock.h"
#include "bsp_uart.h"
#include "sys_api.h"

#include "check_uid.h"
#include "errorno.h"
#include "kv_sys.h"
#include "str_hex.h"
#include "x_strtok.h"
/* Private Includes ----------------------------------------------------------*/
#include "ls_gpio.h"
#include "dfu.h"
#include "ls_syscfg.h"
#include "main.h"
/* Private Define ------------------------------------------------------------*/
#if DFU_SUPPORT
char g_uart_tx_buff[256] = {0};
#define LOG(...)                                                                                             \
//    do {                                                                                                     \
//        bsp_uart_send_nbyte(BSP_UART_0, (uint8_t *)g_uart_tx_buff, snprintf(g_uart_tx_buff, 256, __VA_ARGS__)); \
//    } while (0U)
#else
#define LOG(...)
#endif
/* Private Typedef -----------------------------------------------------------*/
typedef enum {
    FUNC_CODE_FILE_INFO = 1,
    FUNC_CODE_FILE_DATA,
    FUNC_CODE_FILE_CHECK,
    FUNC_CODE_SWO_SWITCH = 0xE0,
    FUNC_CODE_CHIP_KILL = 0xFF,
} dfu_func_code_t;
/* Private Define ------------------------------------------------------------*/
#define PACK_HEAD_LEN       2   // <HEAD 2byte 0xA55A>
#define PACK_INFO_HEAD_LEN  4   // <HEAD 2byte 0xA55A><FUNC 1byte> <LEN 1byte>
#define PACK_INFO_ALL_LEN   5   // <HEAD 2byte 0xA55A><FUNC 1byte> <LEN 1byte> <SUM 1byte>
/* Private Macro -------------------------------------------------------------*/
/* Private Variables ---------------------------------------------------------*/
uint32_t g_write_flash_addr = 0;
// DFU 超时机制 15s没有数据达到，则判断app是否存在，如果存在则跳转至app
static dfu_file_info_t dfu_file_info = {0};
uint32_t g_dfu_tickstart             = 0;
uint16_t g_dfu_timeout_ms            = 15000;
bool g_dfu_timeout_flag              = 0;  // 0 -- 未检测  1--检测完成
bool g_wait_reset                    = false;
static uint8_t uid[16]           = {0};
static uint16_t g_uid_sum        = 0;

typedef struct {
    uint8_t *data;
    uint8_t len;
    bool  flag;
} dispose_data_info_t;

dispose_data_info_t g_dispose_data_info = {0};
/* Private Function Prototypes -----------------------------------------------*/
static void dfu_check_file(uint8_t* data, uint16_t len) {
    if (dfu_file_info.first_rx == false || dfu_file_info.file_size == 0 ||
        dfu_file_info.file_size != dfu_file_info.current_size || g_write_flash_addr == 0) {
        LOG("UP^FAIL,FILE\r\n");
        return;
    }

    uint8_t cal_sum = compute_checksum((uint8_t*)g_write_flash_addr, dfu_file_info.current_size);
    if (cal_sum != dfu_file_info.file_sum) {
        // LOG("UP^FAIL,%x %x\r\n", cal_sum, dfu_file_info.file_sum);
        LOG("UP^FAIL,sum\r\n");
    } else {
        LOG("UP^OK,UP\r\n");
        g_boot_info.app_file_sum = dfu_file_info.file_sum;
        g_boot_info.boot_carry_size = dfu_file_info.current_size;
        if (g_write_flash_addr == LS_FLASH_OTA_ADDR) {
            g_boot_info.boot_state = BOOT_STATE_MOVE_OTA_IN_APP;
        } else if (g_boot_info.boot_state < 2) {
            g_boot_info.boot_state = BOOT_STATE_RUN_APP;
        } else
            return;
        kv_set_env(LS_KV_KEY_BOOT_INFO, (uint8_t*)&g_boot_info, sizeof(boot_info_t));
        g_wait_reset = true;
    }
}



static void atcmd_chip_lock(uint8_t* data, uint16_t len) {
    uint16_t uid_sum = 0;
    if (len < 2)
        return;
    // 得到上位机发送的校验值
    uid_sum = (*data) << 8 | (*(data + 1));

    g_boot_info.boot_state = BOOT_STATE_RUN_APP;
    g_boot_info.chip_lock = uid_sum;  // 填写特殊码解锁烧录
    kv_set_env(LS_KV_KEY_BOOT_INFO, (uint8_t*)&g_boot_info, sizeof(boot_info_t));
    // 判断上位机的校验值于自身校验值是否匹配
    if (g_uid_sum == uid_sum) {
        LOG("AT^OK,UNLOCK\r\n");
        g_wait_reset = true;
    } else {
#if LS_OFF_SWD_FUNC
        // 关闭SWD功能 配置PC7和PD1的端子功能模式  周边模块功能模式使能
        RCC->UNLOCK = 0x55AA6699;
        RCC->SWDIOCR = (0x5A69 << RCC_SWDIOCR_KEY_Pos);
        RCC->UNLOCK = 0x55AA6698;
        LOG("AT^OK,LOCK\r\n");
#else
        LOG("AT^OK,Not Lock Func,%04x|%04x\r\n", g_uid_sum, uid_sum);
#endif
    }
}

static void dfu_file_info_data(uint8_t* data, uint8_t len) {
    memset(&dfu_file_info, 0, sizeof(dfu_file_info_t));
    memcpy(&dfu_file_info, data, len);
    // 计算出密钥值,该key主要用于计算pack_id
    dfu_file_info.secret_key = (uint8_t)(g_uid_sum * 2 + dfu_file_info.timestamp);
    dfu_file_info.first_rx = true;
    uint8_t tx_data[7] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x0A};
    tx_data[2] = (uint8_t)bsp_uart_get_rxbuff_size(BSP_UART_0);
    tx_data[3] = (uint8_t)g_uid_sum;
    tx_data[4] = (uint8_t)g_uid_sum >> 8;
    bsp_uart_send_nbyte(BSP_UART_0, tx_data, 7);
}

static void decode_data(uint8_t* data, uint16_t len, uint8_t secret_key) {
    for (uint16_t i = 0; i < len; i++) {
        if (i % 2 == 0)
            data[i] ^= ((secret_key >> 1) + 1);
        else
            data[i] ^= ((secret_key * i)%256 - 1);
    }
}

static void dfu_file_data(uint8_t* data, uint16_t len) {
    // <pack_id> <DATA0> ... <DATAN>
    uint8_t tx_data[5] = {0x00, 0x00, 0x00, 0x0D, 0x0A};
    if (len < 2)
        return;
    // 解密 pack_id
    decode_data(&data[0], 1, dfu_file_info.secret_key);
    uint8_t pack_id = data[0];
    if ((pack_id == dfu_file_info.old_pack_id) ||
        (pack_id < dfu_file_info.old_pack_id) ||
        (pack_id > dfu_file_info.old_pack_id && (pack_id - dfu_file_info.old_pack_id != 1))) {
        tx_data[0] = E_MSG;
        tx_data[1] = pack_id;
        tx_data[2] = dfu_file_info.old_pack_id;
        decode_data(tx_data, 3, dfu_file_info.secret_key + pack_id);
        bsp_uart_send_nbyte(BSP_UART_0, tx_data, 5);
        return;
    }

    uint8_t temp_key = dfu_file_info.secret_key * data[0] + 81;
    // 解密数据
    decode_data(&data[1], len - 1, temp_key);
    // 写入flash
    // 第一次接到数据，则擦除flash
    if (dfu_file_info.first_rx == true && dfu_file_info.file_size != 0) {
        dfu_file_info.first_rx = false;
        // 如果固件大小 < APP容量的一半，则使用备份升级
        if (dfu_file_info.file_size < (LS_FLASH_APP_SIZE / 2)) {
            g_write_flash_addr = LS_FLASH_OTA_ADDR;
            sys_disable_irq();
            bsp_flash_erase_page(g_write_flash_addr, ((LS_FLASH_APP_SIZE / 2) / LS_FLASH_PAGE_SIZE) + 1);
            sys_enable_irq();
        } else {
            g_write_flash_addr = LS_FLASH_APP_ADDR;
            sys_disable_irq();
            bsp_flash_erase_page(g_write_flash_addr, (LS_FLASH_APP_SIZE / LS_FLASH_PAGE_SIZE) + 1);
            sys_enable_irq();
        }
        //delay_ms(50);
    }


    dfu_file_info.old_pack_id = pack_id;

    if (dfu_file_info.first_rx == false && dfu_file_info.file_size != 0) {
        if (g_write_flash_addr == 0) {
            tx_data[0] = E_INVAL_ADDR;
            decode_data(tx_data, 3, dfu_file_info.secret_key + pack_id);
            bsp_uart_send_nbyte(BSP_UART_0, tx_data, 5);
            return;
        }
        uint8_t pack_len = len - 1;
        // 写入flash中
        bsp_flash_write_nbyte_s(g_write_flash_addr + dfu_file_info.current_size, &data[1], pack_len);
        dfu_file_info.current_size += (pack_len);
        decode_data(tx_data, 3, dfu_file_info.secret_key + pack_id);
        bsp_uart_send_nbyte(BSP_UART_0, tx_data, 5);
    } else {
        LOG("E_FORMAT\r\n");
    }
}

static void dispose_data(uint8_t *data, uint8_t len) {
    switch (data[0]) {
        case FUNC_CODE_FILE_INFO:
            dfu_file_info_data(&data[2], data[1]);
            break;
        case FUNC_CODE_FILE_DATA:
            dfu_file_data(&data[2], data[1]);
            break;
        case FUNC_CODE_FILE_CHECK:
            break;
        case FUNC_CODE_SWO_SWITCH:
            //atcmd_chip_lock((buff + strlen("AT^LOCK=")), 2);
            break;
        case FUNC_CODE_CHIP_KILL:
             bsp_flash_erase_page(LS_FLASH_APP_ADDR, (LS_FLASH_APP_SIZE / LS_FLASH_PAGE_SIZE) + 1);
             bsp_flash_erase_page(LS_KV_BASE_ADDR, LS_FLASH_KV_PAGE);
             bsp_flash_erase_page(LS_FLASH_START_ADDR + LS_FLASH_PAGE_SIZE, (LS_FLASH_BOOT_SIZE / LS_FLASH_PAGE_SIZE) + 1);
            break;
        default:
            break;
    }
}

static void uart0_rx_callback(uint8_t data) {
    uint16_t len = bsp_uart_get_rxbuff_position(BSP_UART_0);
    if (len == 1 && data != 0xA5)
        goto end;
    else if (len == 2 && data != 0x5A)
        goto end;
    else if (len == 4) {// 记录当前pack数据长度
        g_dispose_data_info.len = data;
        return;
    } else if (len > (LS_UART0_CACHE_SIZE )) {
        goto end;
    }

    // 接收长度达到PACK_INFO_HEAD_LEN + pack_len + 1，即SUM位，开始解析数据。
    else if (len > PACK_INFO_ALL_LEN && len == PACK_INFO_HEAD_LEN + g_dispose_data_info.len + 1) {
        uint8_t* buff = bsp_uart_get_rxbuff(BSP_UART_0);
        uint8_t sum = compute_checksum(buff + PACK_INFO_HEAD_LEN, g_dispose_data_info.len);
        if (sum == data) {
            // 校验通过, 填充数据，置标志位，由 dfu_main() 处理数据
            g_dispose_data_info.data = buff + PACK_HEAD_LEN;
            g_dispose_data_info.flag = true;
            return;
        } else {
            uint8_t tx_data[5] = {0x00, 0x00, 0x00, 0x0D, 0x0A};
            tx_data[0] = E_CRC;
            tx_data[1] = sum;
            tx_data[2] = data;
            bsp_uart_send_nbyte(BSP_UART_0, tx_data, 5);
        }
    } else
        return;
end:
        g_dispose_data_info.len = 0;
        bsp_uart_reset_rxbuff(BSP_UART_0);
}

/* Public Function Prototypes -----------------------------------------------*/
void dfu_main(void) {
    uint32_t dfu_tick_current = 0;
    uint16_t tick_led         = 0;
    bool key_flag             = false;
    uint8_t key_tick          = false;
    bool erase_flag           = false;
    get_uid(uid);
    g_uid_sum = compute_checksum(uid, 16);
#if DFU_SUPPORT
    bsp_uart_rx_irq_callback(BSP_UART_0, uart0_rx_callback);
    bsp_uart_init(BSP_UART_0, LS_UART0_BAUD_RATE, 6, 5);
    memset(&dfu_file_info, 0, sizeof(dfu_file_info_t));
#endif
    LOG("UP^OK,dfu mode,%04x\r\n", g_uid_sum);
    delay_ms(5);
    LOG("UP^OK,dfu mode,%04x\r\n", g_uid_sum);
    g_dfu_tickstart = HAL_GetTick();
#if LS_LED_IND_LIGHT_SUPPORT
    bsp_gpio_set_clk(GPIO_APBx, LS_LED0_GPIO_CLK, true);
    bsp_gpio_init_output(LS_LED0_GPIO_PORT, LS_LED0_PIN, BSP_GPIO_PIN_OUT_PP);
#endif
    while (1) {
        if (g_wait_reset) {
            delay_ms(5);
            sys_reset();
        }
        if (g_dispose_data_info.flag) {
            dispose_data(g_dispose_data_info.data, g_dispose_data_info.len);
            memset(&g_dispose_data_info, 0, sizeof(dispose_data_info_t));
            bsp_uart_reset_rxbuff(BSP_UART_0);
        }

#if LS_TIMEOUT_SUPPORT
        // 超时机制
        if ((g_boot_info.boot_state == BOOT_STATE_IN_DFU) && (!g_dfu_timeout_flag)) {
            dfu_tick_current = HAL_GetTick();
            if (dfu_tick_current - g_dfu_tickstart > LS_BOOT_TIMEOUT_TIME) {
                g_dfu_timeout_flag = true;
                sys_jump_app();
            }
        }
#endif
#if LS_LED_IND_LIGHT_SUPPORT
        tick_led++;
        if (tick_led % 0xFFF == 0) {
            // boot 模式 指示灯闪烁
            bsp_gpio_set_toggle(LS_LED0_GPIO_PORT, LS_LED0_PIN);
        }
#endif
        if (!g_dfu_timeout_flag) {
            // boot 模式进行自毁工作
            if (erase_flag) {
                bsp_flash_erase_page(LS_FLASH_APP_ADDR, (LS_FLASH_APP_SIZE / LS_FLASH_PAGE_SIZE) + 1);
                bsp_flash_erase_page(LS_KV_BASE_ADDR, LS_FLASH_KV_PAGE);
                sys_reset();
            }
#if LS_BUTTON_ENTRY_BOOT_SUPPORT
            if (bsp_gpio_get_state(BOARD_BUTTON_SYS_PORT, BOARD_BUTTON_SYS_PIN) == BOARD_BUTTON_SYS_PRESS_LEVEL) {
                if (!key_flag) {
                    continue;
                } else {
                    key_tick++;
                    key_flag = false;
                    if (key_tick > 9) {
                        key_tick = 0;
                        erase_flag = true;
                    }
                }
            } else {
                if (!key_flag) {
                    key_flag = true;
                }
            }
#endif
        }
    }
}
